import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

/**
 * Created with IntelliJ IDEA.
 * Description: TCP回显服务器
 */
public class TcpEchoServer {
    //先创建一个TCP服务端的Socket的套接字
    private ServerSocket serverSocket = null;

    //构造方法与UDP类似
    //参数传递端口号
    public TcpEchoServer(int port) throws IOException {
        this.serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动！");
        while(true) {
            // TCP的服务器要想和客户端进行网络通信，必须先建立连接
            // 而accept()方法就是用来接收客户端请求连接的信息的
            // 接收请求之后，accept会返回一个Socket对象
            // 这个对象才是具体和对应的客户端进行通信的实例
            Socket clientSocket = serverSocket.accept();
            //处理连接
            //如果不用多线程，那么服务器一次只能处理一个请求，只能和一个客户端进行网络通信
            //因为processConnection只是一个方法，而方法内部在循环接收唯一的客户端的请求
            Thread t = new Thread(() -> processConnection(clientSocket));
            t.start();
        }
    }

    private void processConnection(Socket clientSocket) {
        System.out.printf("[%s : %d] 客户端建立连接\n",clientSocket.getInetAddress().toString(), clientSocket.getPort());
        //要处理连接，就要先获得请求的输入流和输出流对象
        try(InputStream inputStream = clientSocket.getInputStream()) {
            try(OutputStream outputStream = clientSocket.getOutputStream()) {
                //为了方便的得到请求转成的字符串，我们直接用Scanner来接收
                Scanner scanner = new Scanner(inputStream);
                //接着循环处理请求
                while(true) {
                    //当没有请求的时候，程序执行完毕
                    if(!scanner.hasNext()) {
                        System.out.printf("[%s : %d] 客户端断开连接\n",clientSocket.getInetAddress(), clientSocket.getPort());
                        break;
                    }
                    //获取请求
                    String request = scanner.next();
                    //处理请求
                    String respond = process(request);
                    //发送请求，为了发送的方便，我们使用PrintWriter类对outputStream进行封装
                    PrintWriter printWriter = new PrintWriter(outputStream);
                    printWriter.println(respond);
                    //刷新缓冲区，让客户端第一时间看到反馈
                    printWriter.flush();
                    System.out.printf("[%s : %d] req: %s, resp: %s\n", clientSocket.getInetAddress().toString(), clientSocket.getPort(), request, respond);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            //此处clientSocket必须释放，因为服务器和该客户端已经断开连接了
            //就不再让它占用资源
            try {
                clientSocket.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer tcpEchoServer = new TcpEchoServer(9090);
        tcpEchoServer.start();
    }
}
